package com.wl.dzt.sys.core.dao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;


import com.wl.dzt.sys.core.dao.curd.CrudCascade;
import com.wl.dzt.sys.core.dao.curd.CrudCommand;
import com.wl.dzt.sys.core.dao.curd.TransactionHandler;
import com.wl.dzt.sys.core.dao.curd.sql.SqlSearchResult;
import com.wl.dzt.sys.core.dao.curd.validate.Validate;
import com.wl.dzt.sys.core.dao.curd.validate.Validatetor;
import com.wl.dzt.sys.core.dao.select.Select;
import com.wl.dzt.sys.core.dao.select.term.Term;
import com.wl.dzt.sys.util.FieldUtil;
import com.wl.dzt.sys.util.SpringUtil;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author wl_code@163.com
 * @date 2018/7/5/005
 * @version 1.0
 * @description 通用DAO
 * 
 */
@Repository("dao")
@Transactional
public class JDao {

	/**
	 * 数据库命令
	 */
	private CrudCommand command;

	/**
	 * 验证对象
	 */
	@Resource
	private Validatetor validatetor;

	/**
	 * <b>插入数据</b>
	 * <p>
	 * 根据实体传递添加一条数据,如果拥有ID则不自动生成UUID
	 * </p>
	 *
	 * @param entity
	 */
	public void insert(Object entity) {
		command.insert(entity);
	}

	/**
	 * <b>保存</b>
	 * <p>
	 * 根据实体保存数据
	 * </p>
	 *
	 * @param entity
	 * @param validates
	 * @return
	 */
	public boolean save(Object entity, Validate... validates) {
		// 根据外键对象进行级联保存（父级）
		boolean success = validatetor.excuteValidate(entity, validates) == null ? (maintainEnttityCascade(entity) ? (DB
				.exitId(entity) ? command.update(entity) : command.insert(entity)) : false) : false;
		// 根据set进行级联保存（子集）
		if (success) {
			maintainEntityCascadeBySet(entity);
		}
		return success;
	}

	/**
	 * <b>保存并返回详情</b>
	 * <p>
	 * 根据验证条件保存数据<br>
	 * 验证失败的时候返回错误信息
	 * </p>
	 *
	 * @param entity
	 * @param validates
	 * @return
	 */
	public List<Validate> saveByDetailed(Object entity, Validate... validates) {
		List<Validate> list = validatetor.excuteValidate(entity, validates);
		if (list != null) {
			return list;
		}
		if (!maintainEnttityCascade(entity)) {
			list = new ArrayList<>();
		}
		if (!(DB.exitId(entity) ? command.update(entity) : command.insert(entity))) {
			list = new ArrayList<>();
		}
		if (list == null) {
			maintainEntityCascadeBySet(entity);
		}
		return list;
	}

	/**
	 * <b>删除</b>
	 * <p>
	 * 删除一个实体
	 * </p>
	 *
	 * @param clazz
	 * @param id
	 * @param validates
	 * @return
	 */
	public boolean delete(Class<?> clazz, String id, Validate... validates) {
		try {
			Object entity = clazz.newInstance();
			DB.setId(entity, id);
			// 根据set进行级联删除
			maintainEntityCascadeBySetDelete(entity);
			return validatetor.excuteValidate(entity, validates) == null ? (command.delete(clazz, id)) : false;
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * <b>删除并且返回详情 </b>
	 * <p>
	 * 根据验证条件删除数据<br>
	 * 删除失败的时候返回错误信息
	 * </p>
	 *
	 * @param clazz
	 * @param id
	 * @param validates
	 * @return
	 */
	public List<Validate> deleteByDetailed(Class<?> clazz, String id, Validate... validates) {
		List<Validate> list = null;
		try {
			Object entity = clazz.newInstance();
			DB.setId(entity, id);
			list = validatetor.excuteValidate(entity, validates);
			if (list != null) {
				return list;
			}
			if (list == null) {
				maintainEntityCascadeBySetDelete(entity);
			}
			if (!command.delete(clazz, id)) {
				list = new ArrayList<>();
				SpringUtil.rollback();
			}
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * <b>查询数据</b>
	 * <p>
	 * 根据条件查询集合数据
	 * </p>
	 *
	 * @param select
	 * @return
	 */
	public <T> List<T> list(Select select) {
		select.fields(FieldUtil.entityValueFields(select.getClazz()));
		return command.select(select);
	}

	/**
	 * <b>不适配查询 </b>
	 * <p>
	 * 根据查询条件查询数据<br>
	 * 不会自动装箱成实体,以便提高性能
	 * </p>
	 *
	 * @param select
	 * @return
	 */
	public List<Map<String, Object>> listByMap(Select select) {
		select.fields(FieldUtil.entityValueFields(select.getClazz()));
		return command.selectByMap(select);
	}

	/**
	 * <b>根据id获取某个实体</b>
	 * <p>
	 * 多级时候需要指定父级字段
	 * </p>
	 *
	 * @param clazz
	 * @param id
	 * @param fields
	 * @return
	 */
	public <T> T get(Class<?> clazz, String id, String... fields) {
		return get(clazz, new Term[] { Term.eq("id", id) }, fields);
	}

	/**
	 * <b>根据条件查找某个实体</b>
	 * <p>
	 * 多级时候需要指定父级字段
	 * </p>
	 *
	 * @param clazz
	 * @param terms
	 * @param fields
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(Class<?> clazz, Term[] terms, String... fields) {
		List<String> fs = new ArrayList<>();
		fs.addAll(Arrays.asList(FieldUtil.entityValueFields(clazz)));
		fs.addAll(Arrays.asList(fields));
		List<?> list = command.select(Select.create(clazz).fields(fs.toArray(new String[fs.size()])).where(terms));
		return list.isEmpty() ? null : (T) list.get(0);
	}

	/**
	 * 
	 * <b>根据id查找</b>
	 * <p>
	 * 返回map
	 * </p>
	 *
	 * @param id
	 * @param fields
	 * @return
	 */
	public Map<String, Object> getByMap(Class<?> clazz, String id, String... fields) {
		List<String> fs = new ArrayList<>();
		fs.addAll(Arrays.asList(FieldUtil.entityValueFields(clazz)));
		fs.addAll(Arrays.asList(fields));
		List<Map<String, Object>> list = command.selectByMap(Select.create(clazz).fields(fs.toArray(new String[fs.size()]))
				.where(Term.eq("id", id)));
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	/**
	 * <b>查看总数</b>
	 * <p>
	 * 根据查询条件查询某个表的总数
	 * </p>
	 *
	 * @param select
	 * @return
	 */
	public Integer count(Select select) {
		return command.count(select);
	}

	/**
	 * <b>执行sql</b>
	 * <p>
	 * 通过sql和 {?} 占位符执行一段sql
	 * </p>
	 * 
	 * @param sql
	 * @param args
	 * @return
	 */
	public List<Map<String, Object>> executeSql(String sql, Object... args) {
		return command.getJdbcTemplate().queryForList(sql, args);
	}

	/**
	 * <b>获取jdbctemplate</b>
	 * <p>
	 * 获取spring的jdbctemplate
	 * </p>
	 * 
	 * @return
	 */
	public JdbcTemplate getJdbcTemplate() {
		return command.getJdbcTemplate();
	}

	/**
	 * <b>开始事务</b>
	 * <p>
	 * 开始一组事务,返回fase即回滚
	 * </p>
	 *
	 * @param transaactionHandler
	 * @return
	 */
	public boolean startTx(TransactionHandler transaactionHandler) {
		return transaactionHandler.handler() ? true : SpringUtil.rollback();
	}

	/**
	 * <b>设置命令器</b>
	 * <p>
	 * 根据数据库类型自动填充数据库命令bean
	 * </p>
	 * 
	 * @param command
	 */
	public void setCommand(CrudCommand command) {
		this.command = command;
	}

	/**
	 * 
	 * <b> 设置验证器 </b>
	 * <p>
	 * 填充验证对象
	 * </p>
	 * 
	 * @param validatetor
	 */
	public void setValidatetor(Validatetor validatetor) {
		this.validatetor = validatetor;
	}

	/**
	 * <b>维护数据完整性 </b>
	 * <p>
	 * 对象内一个值都没有则略过。<br>
	 * 对象内存在ID则修改，空值不做操作。<br>
	 * 对象内不存在ID但是存在其他字段则添加。<br>
	 * </p>
	 * 
	 * @param entity
	 * @return
	 */
	private boolean maintainEnttityCascade(Object entity) {
		return this.startTx(() -> {
			List<?> data = CrudCascade.cascadeField(entity);
			for (Object e : data) {
				if (DB.idOtherFields(e) || DB.isEmptyEntity(e))
					continue;
				if (!(DB.exitId(e) ? command.update(e) : command.insert(e)))
					return false;
			}
			return true;
		});
	}

	/**
	 * 
	 * <b>维护数据完整性</b>
	 * <p>
	 * 根据set 维护子集。<br>
	 * 如果set中存在数据，再保存时候则添加set中数据，修改时删除当前实体所有的本set所对应的实体的子表，再添加set中子集。<br>
	 * </p>
	 * 
	 * @param entity
	 * @return
	 */
	private boolean maintainEntityCascadeBySet(Object entity) {
		List<FieldUtil.RefSetInfo> list = FieldUtil.exitsSet(entity, 1);
		if (list.isEmpty()) {
			return true;
		}
		// 如果是修改则删除掉原来的关联
		for (FieldUtil.RefSetInfo ref : list) {
			if (ref.getSet() != null) {
				List<?> deleteEntitys = this.list(Select.create(ref.getSrcClass()).where(
						Term.eq(ref.getRefField() + ".id", FieldUtil.doGetMethod(entity, "id"))));
				deleteEntitys.forEach(deleteEntity -> this.delete(deleteEntity.getClass(), FieldUtil.doGetMethod(deleteEntity, "id").toString()));
				for (Object obj : ref.getSet()) {
					BeanMap beanMap = BeanMap.create(obj);
					beanMap.put(ref.getRefField(), entity);
					this.save(obj);
				}
			}
		}

		return true;
	}

	/**
	 * <b>维护数据完整性 </b>
	 * <p>
	 * 根据set 级联删除子集。
	 * </p>
	 * 
	 * @param entity
	 * @return
	 */
	private boolean maintainEntityCascadeBySetDelete(Object entity) {
		List<FieldUtil.RefSetInfo> list = FieldUtil.exitsSet(entity, -1);
		if (list.isEmpty()) {
			return true;
		}
		for (FieldUtil.RefSetInfo ref : list) {
			List<?> deletes = this.list(Select.create(ref.getSrcClass()).where(
					Term.eq(ref.getRefField() + ".id", FieldUtil.doGetMethod(entity, "id"))));
			deletes.forEach(d -> this.delete(ref.getSrcClass(), FieldUtil.doGetMethod(d, "id").toString()));
		}
		return true;
	}

	/**
	 * <b>sql表达式查询</b>
	 * <p>
	 * 根据sql结合条件占位符进行查询
	 * </p>
	 * 
	 * @param select
	 * @return
	 */
	public SqlSearchResult selectBySqlExp(Select select) {
		return command.selectBySqlExp(select);
	}

}
